import tkinter as tk
from tkinter import ttk, messagebox, filedialog, scrolledtext
import threading
import os
from urllib.parse import urlparse
import re

class DouyinVideoDownloaderGUI:
    def __init__(self, root):
        self.root = root
        self.current_video_title = None
        self.setup_ui()
        self.center_window()  # 居中显示窗口
        
    def center_window(self):
        """将窗口居中显示"""
        self.root.update_idletasks()
        width = self.root.winfo_width()
        height = self.root.winfo_height()
        x = (self.root.winfo_screenwidth() // 2) - (width // 2)
        y = (self.root.winfo_screenheight() // 2) - (height // 2)
        self.root.geometry(f'{width}x{height}+{x}+{y}')
        
    def setup_ui(self):
        self.root.title("抖音视频去水印下载器")
        self.root.geometry("700x600")
        self.root.resizable(True, True)
        
        # 创建主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 标题
        title_label = ttk.Label(main_frame, text="抖音视频无损去水印", font=("Arial", 16, "bold"))
        title_label.pack(pady=(0, 20))
        
        # URL输入区域
        url_frame = ttk.LabelFrame(main_frame, text="视频链接", padding="10")
        url_frame.pack(fill=tk.X, pady=(0, 10))
        
        ttk.Label(url_frame, text="抖音分享链接:").pack(anchor=tk.W)
        self.url_entry = ttk.Entry(url_frame, width=70)
        self.url_entry.pack(fill=tk.X, pady=(5, 0))
        self.url_entry.insert(0, "按鼠标右键粘贴剪贴版抖音视频链接")
        self.url_entry.bind("<FocusIn>", self.clear_url_placeholder)
        self.url_entry.bind("<FocusOut>", self.add_url_placeholder)
        # 添加右键粘贴功能
        self.url_entry.bind("<Button-3>", self.paste_url)  # 鼠标右键粘贴
        
        # 示例链接
        example_label = ttk.Label(url_frame, text="完整复制抖音分享链接，程序自动会过滤，正确示例URL: https://v.douyin.com/ZdYS0Wa-L6w/", foreground="gray")
        example_label.pack(anchor=tk.W, pady=(5, 0))
        
        # 解析方法选择
        method_frame = ttk.LabelFrame(main_frame, text="解析方法", padding="10")
        method_frame.pack(fill=tk.X, pady=(0, 10))
        
        ttk.Label(method_frame, text="选择解析方法:").pack(anchor=tk.W)
        
        self.method_var = tk.StringVar(value="selenium")
        method_frame_inner = ttk.Frame(method_frame)
        method_frame_inner.pack(fill=tk.X, pady=(5, 0))
        
        ttk.Radiobutton(method_frame_inner, text="浏览器自动化 (推荐)", variable=self.method_var, value="selenium").pack(anchor=tk.W)
        
        # 按钮区域
        button_frame = ttk.Frame(main_frame)
        button_frame.pack(fill=tk.X, pady=(0, 10))
        
        self.download_btn = ttk.Button(button_frame, text="下载视频", command=self.start_download)
        self.download_btn.pack(side=tk.LEFT, padx=(0, 10))
        
        self.clear_btn = ttk.Button(button_frame, text="清空结果", command=self.clear_results)
        self.clear_btn.pack(side=tk.LEFT)
        
        # 进度条
        self.progress = ttk.Progressbar(main_frame, mode='indeterminate')
        self.progress.pack(fill=tk.X, pady=(0, 10))
        
        # 状态标签
        self.status_var = tk.StringVar(value="准备就绪")
        self.status_label = ttk.Label(main_frame, textvariable=self.status_var, foreground="blue")
        self.status_label.pack(anchor=tk.W)
        
        # 结果显示区域
        result_frame = ttk.LabelFrame(main_frame, text="下载结果", padding="10")
        result_frame.pack(fill=tk.BOTH, expand=True, pady=(10, 0))
        
        self.result_text = scrolledtext.ScrolledText(result_frame, height=12, state=tk.DISABLED)
        self.result_text.pack(fill=tk.BOTH, expand=True)
        
    def clear_url_placeholder(self, event):
        if self.url_entry.get() == "请粘贴抖音视频链接":
            self.url_entry.delete(0, tk.END)
            
    def add_url_placeholder(self, event):
        if not self.url_entry.get():
            self.url_entry.insert(0, "请粘贴抖音视频链接")
            
    def paste_url(self, event):
        """右键粘贴URL，自动提取有效的抖音链接"""
        try:
            # 获取剪贴板内容
            clipboard_content = self.root.clipboard_get()
            # 从剪贴板内容中提取抖音链接
            douyin_url = self.extract_douyin_url(clipboard_content)
            if douyin_url:
                self.url_entry.delete(0, tk.END)
                self.url_entry.insert(0, douyin_url)
        except tk.TclError:
            # 剪贴板中没有内容或不是文本
            pass
            
    def extract_douyin_url(self, text):
        """从文本中提取有效的抖音链接"""
        if not text:
            return None
            
        # 抖音链接的正则表达式模式
        patterns = [
            r'https?://v\.douyin\.com/[A-Za-z0-9\-_]+',
            r'https?://www\.douyin\.com/video/\d+'
        ]
        
        # 尝试匹配每个模式
        for pattern in patterns:
            match = re.search(pattern, text)
            if match:
                return match.group(0)
                
        # 如果没有找到匹配的链接，检查是否是完整的URL
        if text.startswith(('http://', 'https://')):
            return text
            
        return None
            
    def start_download(self):
        raw_url = self.url_entry.get().strip()
        if not raw_url or raw_url == "请粘贴抖音视频链接":
            messagebox.showwarning("警告", "请输入有效的抖音视频链接")
            return
            
        # 从输入中提取有效的抖音链接
        url = self.extract_douyin_url(raw_url)
        if not url:
            messagebox.showwarning("警告", "请输入有效的抖音视频链接")
            return
            
        method = self.method_var.get()
            
        # 在新线程中执行下载，避免阻塞UI
        download_thread = threading.Thread(target=self.download_video, args=(url, method))
        download_thread.daemon = True
        download_thread.start()
        
    def update_status(self, message):
        self.status_var.set(message)
        self.root.update_idletasks()
        
    def append_result(self, message):
        """在结果区域添加消息"""
        self.result_text.config(state=tk.NORMAL)
        self.result_text.insert(tk.END, f"{message}\n")
        self.result_text.config(state=tk.DISABLED)
        self.result_text.see(tk.END)
        
    def clear_results(self):
        """清空结果区域"""
        self.result_text.config(state=tk.NORMAL)
        self.result_text.delete(1.0, tk.END)
        self.result_text.config(state=tk.DISABLED)
        
    def extract_video_title(self, page_source):
        """从页面源码中提取视频标题"""
        try:
            import re
            
            # 查找h1标签中的标题
            # 匹配您提供的HTML结构
            h1_pattern = r'<h1[^>]*class="[^"]*idrZUbq7[^"]*"[^>]*>(.*?)</h1>'
            matches = re.findall(h1_pattern, page_source, re.DOTALL)
            
            if not matches:
                # 尝试更通用的h1匹配
                h1_pattern = r'<h1[^>]*>(.*?)</h1>'
                matches = re.findall(h1_pattern, page_source, re.DOTALL)
            
            if matches:
                title_content = matches[0]
                # 清理HTML标签，提取纯文本
                # 移除嵌套的span标签
                clean_title = re.sub(r'<[^>]+>', '', title_content).strip()
                # 移除多余的空白字符
                clean_title = re.sub(r'\s+', ' ', clean_title)
                
                if clean_title:
                    # 进一步清理，移除可能的多余字符
                    clean_title = clean_title.strip()
                    if clean_title:
                        return clean_title
                    
            return None
        except Exception as e:
            self.append_result(f"提取标题时出错: {str(e)}")
            return None
        
    def download_video(self, url, method):
        try:
            self.download_btn.config(state=tk.DISABLED)
            self.progress.start()
            self.update_status("正在解析视频链接...")
            self.append_result(f"开始处理链接: {url}")
            self.append_result(f"使用解析方法: {method}")
            
            video_url = None
            video_id = None
            
            # 提取视频ID
            video_id = self.extract_video_id(url)
            if video_id:
                self.update_status(f"找到视频ID: {video_id}")
                self.append_result(f"视频ID: {video_id}")
            else:
                self.update_status("无法提取视频ID")
                self.append_result("警告: 无法提取视频ID")
            
            # 根据选择的方法进行解析 (只保留selenium方法)
            if method == "selenium":
                video_url = self.get_video_url_selenium(url)
            
            if not video_url:
                self.update_status("无法获取视频链接")
                self.append_result("错误: 无法获取视频链接")
                messagebox.showerror("错误", "无法获取视频链接，请尝试其他解析方法")
                return
                
            self.update_status("正在下载视频...")
            self.append_result(f"视频链接: {video_url[:100]}...")
            
            # 生成默认文件名
            if self.current_video_title:
                # 使用视频标题作为文件名，去除非法字符
                safe_title = self.clean_filename(self.current_video_title)
                filename = f"{safe_title}.mp4"
            else:
                filename = f"douyin_{video_id if video_id else 'video'}.mp4"
            
            # 保存在程序运行目录内
            import os
            program_dir = os.path.dirname(os.path.abspath(__file__))
            save_path = os.path.join(program_dir, filename)
            
            # 检查文件是否已存在，如果存在则添加序号
            base_name, ext = os.path.splitext(save_path)
            counter = 1
            original_path = save_path
            while os.path.exists(save_path):
                save_path = f"{base_name}_{counter}{ext}"
                counter += 1
            
            self.append_result(f"视频将保存到: {save_path}")
            
            if not save_path:
                self.update_status("用户取消保存")
                self.append_result("用户取消保存")
                return
                
            # 下载视频
            success = self.save_video(video_url, save_path)
            
            if success:
                self.update_status("下载完成!")
                self.append_result(f"成功下载视频: {save_path}")
                messagebox.showinfo("成功", f"视频已保存到: {save_path}")
            else:
                self.update_status("下载失败")
                self.append_result("错误: 视频下载失败")
                messagebox.showerror("错误", "视频下载失败")
                
        except Exception as e:
            self.update_status(f"发生错误: {str(e)}")
            self.append_result(f"错误: {str(e)}")
            messagebox.showerror("错误", f"下载过程中发生错误: {str(e)}")
        finally:
            self.progress.stop()
            self.download_btn.config(state=tk.NORMAL)
            
    def extract_video_id(self, url):
        """从抖音链接中提取视频ID"""
        patterns = [
            r'https?://v\.douyin\.com/([A-Za-z0-9\-_]+)/?',
            r'https?://www\.douyin\.com/video/(\d+)',
        ]
        
        for pattern in patterns:
            match = re.search(pattern, url)
            if match:
                return match.group(1)
        return None
        
    def get_video_url_selenium(self, url):
        """使用Selenium方法获取视频链接"""
        try:
            self.update_status("正在启动浏览器...")
            self.append_result("使用Selenium方法解析...")
            
            # 导入Selenium相关模块
            from selenium import webdriver
            from selenium.webdriver.common.by import By
            from selenium.webdriver.chrome.options import Options
            from selenium.webdriver.chrome.service import Service
            import time
            
            # 设置Chrome选项
            chrome_options = Options()
            chrome_options.add_argument('--no-sandbox')
            chrome_options.add_argument('--disable-dev-shm-usage')
            chrome_options.add_argument('--disable-blink-features=AutomationControlled')
            chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
            chrome_options.add_experimental_option('useAutomationExtension', False)
            chrome_options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36')
            
            # 设置ChromeDriver和Chrome浏览器路径
            chrome_driver_path = "./chromedriver.exe"
            chrome_binary_path = "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe"
            chrome_options.binary_location = chrome_binary_path
            
            # 启动浏览器
            service = Service(chrome_driver_path)
            driver = webdriver.Chrome(service=service, options=chrome_options)
            
            try:
                # 访问分享链接
                driver.get(url)
                
                # 等待页面加载
                time.sleep(5)
                
                # 滚动页面以触发视频加载
                driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
                time.sleep(2)
                
                # 获取页面标题
                page_source = driver.page_source
                video_title = self.extract_video_title(page_source)
                if video_title:
                    self.append_result(f"视频标题: {video_title}")
                    # 保存标题供后续使用
                    self.current_video_title = video_title
                
                # 获取视频元素
                video_elements = driver.find_elements(By.TAG_NAME, "video")
                
                if video_elements:
                    video_element = video_elements[0]  # 获取第一个视频元素
                    
                    # 检查source子元素
                    source_elements = video_element.find_elements(By.TAG_NAME, "source")
                    for source_element in source_elements:
                        source_src = source_element.get_attribute("src")
                        if source_src and source_src.startswith("http"):
                            driver.quit()
                            self.append_result("通过Selenium找到视频链接")
                            return source_src
                    
                    # 检查currentSrc属性
                    current_src = video_element.get_attribute("currentSrc")
                    if current_src and current_src.startswith("http"):
                        driver.quit()
                        self.append_result("通过Selenium找到视频链接")
                        return current_src
                        
                driver.quit()
                self.append_result("Selenium方法未找到视频链接")
                return None
            except Exception as e:
                driver.quit()
                self.append_result(f"Selenium执行错误: {str(e)}")
                return None
                
        except ImportError:
            self.append_result("错误: 未安装Selenium库")
            return None
        except Exception as e:
            self.append_result(f"Selenium方法错误: {str(e)}")
            return None
            
    def save_video(self, video_url, save_path):
        """下载并保存视频"""
        try:
            import requests
            
            # 设置请求头
            headers = {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36',
                'Referer': 'https://www.douyin.com/',
            }
            
            # 不使用代理
            proxies = {
                'http': None,
                'https': None
            }
            
            response = requests.get(video_url, headers=headers, stream=True, timeout=30, proxies=proxies)
            response.raise_for_status()
            
            total_size = int(response.headers.get('content-length', 0))
            downloaded_size = 0
            
            with open(save_path, 'wb') as f:
                for chunk in response.iter_content(chunk_size=8192):
                    if chunk:
                        f.write(chunk)
                        downloaded_size += len(chunk)
                        if total_size > 0:
                            progress = (downloaded_size / total_size) * 100
                            self.update_status(f"下载进度: {progress:.1f}%")
                            # 更新进度条
                            self.progress['value'] = progress
                            self.root.update_idletasks()
                            
            return True
        except Exception as e:
            self.append_result(f"保存视频时出错: {str(e)}")
            return False
            
    def clean_filename(self, filename):
        """清理文件名中的非法字符"""
        # Windows文件名中不允许的字符: \ / : * ? " < > |
        illegal_chars = r'[\\/:*?"<>|]'
        cleaned = re.sub(illegal_chars, '_', filename)
        # 限制文件名长度
        if len(cleaned) > 100:
            cleaned = cleaned[:100]
        # 去除首尾空格
        cleaned = cleaned.strip()
        # 如果文件名为空，返回默认名称
        if not cleaned:
            cleaned = "douyin_video"
        return cleaned


if __name__ == "__main__":
    root = tk.Tk()
    app = DouyinVideoDownloaderGUI(root)
    root.mainloop()